use std::any::Any;
use std::collections::HashMap;
use std::ops::Add;
use std::sync::{Arc, RwLock};
use std::time::{Duration, Instant};
use crate::codec::RpcCodecGather;
use crate::{LocalSeqCreator, RpcResult};
use crate::error::{CustomerErrorKind, RpcError};

#[derive(Clone)]
pub struct RpcSession {
    data: Arc<RwLock<HashMap<String, SessionDataItem>>>,
    current_codec_type: Arc<RwLock<Option<String>>>,
    rpc_codec_map: RpcCodecGather,
    local_seq_creator: Arc<Box<dyn LocalSeqCreator>>,
}

struct SessionDataItem {
    data: Box<dyn Any>,
    expired_time: Instant, // 过期时间， 默认100年后过期
}

impl RpcSession {

    pub fn new(local_seq_creator: Arc<Box<dyn LocalSeqCreator>>, rpc_codec_map: RpcCodecGather) -> Self {
        RpcSession {
            data: Arc::new(RwLock::new(HashMap::new())),
            current_codec_type: Arc::new(RwLock::new(None)),
            rpc_codec_map,
            local_seq_creator,
        }
    }

    pub fn with_data<T: 'static, R>(&mut self, key: &str, f: impl FnOnce(&mut T) -> R) -> Option<R> {
        let mut guard = self.data.write().unwrap();
        let data = guard.get_mut(key);
        if let Some(data) = data && is_expired(data) {
            let data = &mut data.data;
            let value = data.downcast_mut::<T>();
            match value {
                None => { None }
                Some(value) => { Some(f(value)) }
            }
        } else {
            None
        }
    }

    pub fn read_data<T: 'static, R>(&self, key: &str, f: impl FnOnce(&T) -> R) -> Option<R> {
        let guard = self.data.read().unwrap();
        let data = guard.get(key);
        if let Some(data) = data && is_expired(data) {
            let data = &data.data;
            let value = data.downcast_ref::<T>();
            match value {
                None => { None }
                Some(value) => { Some(f(value)) }
            }
        } else {
            None
        }
    }

    pub fn add_data(&mut self, key: String, value: Box<dyn Any>) {
        let mut write_lock = self.data.write().unwrap();
        let new_session_data_item = SessionDataItem::new(value);
        write_lock.insert(key, new_session_data_item);
    }

    pub fn remove_data(&mut self, key: &str) -> Option<Box<dyn Any>> {
        let mut write_lock = self.data.write().unwrap();
        let data = write_lock.remove(key);
        match data {
            None => { None }
            Some(data) => {
                Some(data.data)
            }
        }
    }

    pub fn set_current_codec_type(&mut self, current_codec_type: Option<String>) -> RpcResult<()> {
        if let Some(codec_type) = current_codec_type {
            if self.rpc_codec_map.has_codec_type(&codec_type) {
                let mut guard = self.current_codec_type.write().unwrap();
                guard.replace(codec_type);
            } else {
                let error = RpcError::CustomerError {error_kid: CustomerErrorKind::CodecTypeNoSetError, message: "codec_type不正确".to_string()};
                return Err(error);
            }
        }
        Ok(())
    }

    pub fn local_seq_creator(&self) -> Arc<Box<dyn LocalSeqCreator>> {
        self.local_seq_creator.clone()
    }

    pub fn get_rpc_codec_gather(&self) -> RpcCodecGather {
        self.rpc_codec_map.clone()
    }

    pub fn get_current_codec_type(&self) -> Option<String> {
        self.current_codec_type.read().unwrap().clone()
    }
}

fn is_expired( session_item_data: &SessionDataItem) -> bool {
    session_item_data.expired_time > Instant::now()
}

impl SessionDataItem {

    fn new(value: Box<dyn Any>) -> Self {
        SessionDataItem {
            data: value,
            expired_time: Instant::now().add(Duration::from_secs(4 * 3600)),  // 4h
        }
    }
}


